package com.c2b.algorithm.leetcode.jzofferII;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <a href="https://leetcode.cn/problems/1fGaJU/description/"> 数组中和为 0 的三个数</a>
 * <p>
 * 给你一个整数数组 nums ，判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ，同时还满足 nums[i] + nums[j] + nums[k] == 0 。<br>
 * 请你返回所有和为 0 且不重复的三元组。<br>
 * 注意：答案中不可以包含重复的三元组。
 * </p>
 *
 * <pre>
 * 示例 1：
 *      输入：nums = [-1,0,1,2,-1,-4]
 *      输出：[[-1,-1,2],[-1,0,1]]
 *      解释：
 *          nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
 *          nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
 *          nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
 *          不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
 *          注意，输出的顺序和三元组的顺序并不重要。
 *
 * 示例 2：
 *      输入：nums = [0,1,1]
 *      输出：[]
 *      解释：唯一可能的三元组和不为 0 。
 *
 *
 * 示例 3：
 *      输入：nums = [0,0,0]
 *      输出：[[0,0,0]]
 *      解释：唯一可能的三元组和为 0 。
 * </pre>
 * <b>提示：</b>
 * <ui>
 * <li>3 <= nums.length <= 3000</li>
 * <li>-10^5 <= nums[i] <= 10^5</li>
 * </ui>
 *
 * @author c2b
 * @since 2023/4/12 16:09
 */
public class Jz007ThreeSum_M {

    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        // 所有数都大于0 或者 所有数都小于0
        if (nums.length < 3 || nums[0] > 0 || nums[nums.length - 1] < 0) {
            return res;
        }
        for (int first = 0; first < nums.length - 2; first++) {
            // 需要和上一次枚举的数不相等，去重操作。
            if (first > 0 && nums[first - 1] == nums[first]) {
                continue;
            }
            // 目标值，即当前值的相反数
            int targetVal = -nums[first];
            // 左右双指针
            int second = first + 1, third = nums.length - 1;
            while (second < third) {
                int currentVal = nums[second] + nums[third];
                if (currentVal > targetVal) {
                    --third;
                } else if (currentVal < targetVal) {
                    ++second;
                } else {
                    // 当找到合适的三个数字以后进行汇总
                    res.add(Arrays.asList(nums[first], nums[second], nums[third]));
                    ++second;
                    --third;
                    // 去重操作
                    while (second < third && nums[second] == nums[second - 1]) {
                        ++second;
                    }
                    while (second < third && nums[third] == nums[third + 1]) {
                        --third;
                    }
                }
            }
        }
        return res;
    }

    public static void main(String[] args) {
        Jz007ThreeSum_M jz007ThreeSum_m = new Jz007ThreeSum_M();
        List<List<Integer>> lists = jz007ThreeSum_m.threeSum(new int[]{0, 0, 0});
        for (List<Integer> list : lists) {
            for (Integer integer : list) {
                System.out.print(integer + "  ");
            }
            System.out.println();
        }
    }
}
